"""
统一日志系统核心模块

提供高性能异步日志处理，支持精确的调用位置捕获、
按天文件轮转、报警机制等功能
"""

import logging
import os
import queue
import sys
import threading
import time
import traceback
from typing import Any, Optional, Dict, Tuple
from . import exceptions
from .config import (
    LOG_DIR, LOG_FILE, LOG_RETENTION_DAYS, LOG_MAX_FILE_SIZE, LOG_BACKUP_COUNT,
    LOG_FORMAT, DATE_FORMAT, LOG_LEVELS, get_log_level, get_file_size_bytes,
    CONSOLE_OUTPUT, CONSOLE_LOG_LEVEL, FILE_OUTPUT, FILE_LOG_LEVEL,
    LOG_ROTATION_ENABLED, LOG_ROTATION_INTERVAL, CALL_LOCATION_ENABLED,
    ASYNC_ENABLED, ASYNC_QUEUE_SIZE, ASYNC_WORKER_THREADS,
    EXCEPTION_TRACE_ENABLED, PERFORMANCE_MONITORING, PERFORMANCE_THRESHOLD_MS,
    LogLevel
)
from .formatters import CleanFilenameFormatter
from .resource_manager import resource_manager
from logging.handlers import TimedRotatingFileHandler, RotatingFileHandler


class CallLocationCapture:
    """调用位置捕获类
    
    在调用点捕获详细的调用栈信息，用于精确定位日志来源
    """
    
    def __init__(self, filename: str, module: str, lineno: int, func_name: str):
        """初始化调用位置信息
        
        Args:
            filename: 文件名
            module: 模块名
            lineno: 行号
            func_name: 函数名
        """
        self.filename = filename
        self.module = module
        self.lineno = lineno
        self.func_name = func_name
    
    @classmethod
    def capture(cls) -> 'CallLocationCapture':
        """捕获当前调用位置
        
        Returns:
            CallLocationCapture: 调用位置信息对象
        """
        if not CALL_LOCATION_ENABLED:
            return cls(filename='disabled', module='disabled', lineno=0, func_name='disabled')
            
        frame = sys._getframe(3)  # 跳过内部调用栈
        
        # 跳过内部模块，找到用户代码
        skip_patterns = [
            'unified_logger',
            'logging',
            'threading',
            'queue'
        ]
        
        max_depth = 8  # 限制最大搜索深度
        current_depth = 0
        
        while frame and current_depth < max_depth:
            filename = frame.f_code.co_filename
            func_name = frame.f_code.co_name
            
            is_user_code = True
            for pattern in skip_patterns:
                if pattern in filename:
                    is_user_code = False
                    break
            
            if is_user_code:
                return cls(
                    filename=os.path.basename(filename),
                    module=frame.f_globals.get('__name__', 'unknown'),
                    lineno=frame.f_lineno,
                    func_name=func_name
                )
            
            frame = frame.f_back
            current_depth += 1
        
        # 如果没有找到用户代码，返回默认值
        return cls(
            filename='unknown',
            module='unknown',
            lineno=0,
            func_name='unknown'
        )


class CallContextFilter(logging.Filter):
    """调用上下文过滤器
    
    将捕获的调用位置信息注入到日志记录中
    """
    
    def filter(self, record: logging.LogRecord) -> bool:
        """过滤日志记录，注入调用位置信息
        
        Args:
            record: 日志记录对象
            
        Returns:
            bool: 始终返回True，表示不丢弃任何日志
        """
        if hasattr(record, '_call_location'):
            call_location = record._call_location
            record.filename = call_location.filename
            record.module = call_location.module
            record.lineno = call_location.lineno
            record.funcName = call_location.func_name
        
        return True


class AlertHandler(logging.Handler):
    """报警处理器
    
    处理需要报警的日志消息，支持多种报警方式
    """
    
    def __init__(self, alert_to_console: bool = True):
        super().__init__()
        self.alert_to_console = alert_to_console
    
    def emit(self, record: logging.LogRecord) -> None:
        """处理报警日志
        
        Args:
            record: 日志记录对象
        """
        if getattr(record, 'alert', False):
            try:
                msg = self.format(record)
                if self.alert_to_console:
                    print(f"🚨 ALERT: {msg}", file=sys.stderr)
            except Exception:
                self.handleError(record)


class PerformanceMonitorFilter(logging.Filter):
    """性能监控过滤器
    
    监控日志记录的性能
    """
    
    def __init__(self):
        super().__init__()
        self.start_time = None
    
    def filter(self, record: logging.LogRecord) -> bool:
        """过滤日志记录，添加性能信息
        
        Args:
            record: 日志记录对象
            
        Returns:
            bool: 始终返回True
        """
        if PERFORMANCE_MONITORING:
            import time
            if self.start_time is None:
                self.start_time = time.time()
            
            elapsed_ms = (time.time() - self.start_time) * 1000
            if elapsed_ms > PERFORMANCE_THRESHOLD_MS:
                record.performance_warning = f"Slow log: {elapsed_ms:.2f}ms"
        
        return True


class LogProcessor:
    """统一日志处理器 - 核心类
    
    提供高性能异步日志处理，支持精确的调用位置捕获、
    文件轮转、报警机制、性能监控等功能
    """
    
    def __init__(self, name: str = 'unified_logger') -> None:
        """初始化日志处理器"""
        self.name = name
        self._init_log_directories()
        self._configure_loggers()
        
        if ASYNC_ENABLED:
            self.log_queue: queue.Queue = queue.Queue(maxsize=ASYNC_QUEUE_SIZE)
            self.worker_thread: Optional[threading.Thread] = None
            self._start_worker_thread()
        else:
            self.log_queue = None
            self.worker_thread = None
    
    def _init_log_directories(self) -> None:
        """创建日志目录
        
        确保日志目录存在，处理权限问题
        """
        try:
            if not os.path.exists(LOG_DIR):
                os.makedirs(LOG_DIR, exist_ok=True)
        except OSError as e:
            raise exceptions.LoggerConfigError(f"无法创建日志目录: {e}")
    
    def _configure_loggers(self) -> None:
        """配置日志处理器
        
        设置日志格式、文件处理器、报警处理器等
        """
        # 创建logger
        self.logger = logging.getLogger(self.name)
        self.logger.setLevel(get_log_level())
        
        # 清除现有的处理器和过滤器
        self.logger.handlers.clear()
        for f in self.logger.filters[:]:
            self.logger.removeFilter(f)
        
        # 添加调用位置过滤器
        context_filter = CallContextFilter()
        self.logger.addFilter(context_filter)
        
        # 添加性能监控过滤器
        if PERFORMANCE_MONITORING:
            performance_filter = PerformanceMonitorFilter()
            self.logger.addFilter(performance_filter)
        
        # 创建格式化器
        formatter = CleanFilenameFormatter(
            fmt=LOG_FORMAT,
            datefmt=DATE_FORMAT
        )
        
        # 控制台处理器
        if CONSOLE_OUTPUT:
            console_handler = logging.StreamHandler()
            console_handler.setLevel(getattr(logging, CONSOLE_LOG_LEVEL))
            console_handler.setFormatter(formatter)
            self.logger.addHandler(console_handler)
        
        # 文件处理器
        if FILE_OUTPUT:
            self._setup_file_handler(formatter)
        
        # 报警处理器
        alert_handler = AlertHandler()
        alert_handler.setFormatter(formatter)
        self.logger.addHandler(alert_handler)
    
    def _setup_file_handler(self, formatter) -> None:
        """配置文件处理器
        
        Args:
            formatter: 日志格式化器
        """
        try:
            if LOG_ROTATION_ENABLED:
                if LOG_ROTATION_INTERVAL == 'midnight':
                    file_handler = TimedRotatingFileHandler(
                        LOG_FILE,
                        when='midnight',
                        interval=1,
                        backupCount=LOG_RETENTION_DAYS,
                        encoding='utf-8'
                    )
                elif LOG_ROTATION_INTERVAL == 'hourly':
                    file_handler = TimedRotatingFileHandler(
                        LOG_FILE,
                        when='H',
                        interval=1,
                        backupCount=LOG_BACKUP_COUNT,
                        encoding='utf-8'
                    )
                elif LOG_ROTATION_INTERVAL == 'daily':
                    file_handler = TimedRotatingFileHandler(
                        LOG_FILE,
                        when='D',
                        interval=1,
                        backupCount=LOG_BACKUP_COUNT,
                        encoding='utf-8'
                    )
                else:  # weekly
                    file_handler = TimedRotatingFileHandler(
                        LOG_FILE,
                        when='W0',
                        interval=1,
                        backupCount=LOG_BACKUP_COUNT,
                        encoding='utf-8'
                    )
            else:
                # 使用大小轮转
                max_bytes = get_file_size_bytes(LOG_MAX_FILE_SIZE)
                file_handler = RotatingFileHandler(
                    LOG_FILE,
                    maxBytes=max_bytes,
                    backupCount=LOG_BACKUP_COUNT,
                    encoding='utf-8'
                )
            
            file_handler.setLevel(getattr(logging, FILE_LOG_LEVEL))
            file_handler.setFormatter(formatter)
            self.logger.addHandler(file_handler)
            
        except Exception as e:
            raise exceptions.LoggerConfigError(f"配置文件处理器失败: {e}")
    
    def _start_worker_thread(self) -> None:
        """启动异步工作线程"""
        if not ASYNC_ENABLED or self.worker_thread is not None:
            return
            
        def _worker():
            """异步工作线程"""
            while True:
                try:
                    log_func = self.log_queue.get(timeout=1)
                    if log_func is None:
                        break
                    log_func()
                    self.log_queue.task_done()
                except queue.Empty:
                    continue
                except Exception:
                    pass
        
        self.worker_thread = threading.Thread(target=_worker, daemon=True)
        self.worker_thread.start()
    
    def _log_async(self, level: int, msg: str, **kwargs) -> None:
        """异步记录日志
        
        Args:
            level: 日志级别
            msg: 日志消息
            **kwargs: 额外参数
        """
        def _log():
            if CALL_LOCATION_ENABLED:
                call_location = CallLocationCapture.capture()
                kwargs['extra'] = kwargs.get('extra', {})
                kwargs['extra']['_call_location'] = call_location
            
            self.logger.log(level, msg, **kwargs)
        
        if ASYNC_ENABLED and self.log_queue is not None:
            try:
                self.log_queue.put_nowait(_log)
            except queue.Full:
                # 队列满时直接记录
                _log()
        else:
            _log()
    
    def debug(self, msg: str, **kwargs) -> None:
        """记录调试日志"""
        self._log_async(logging.DEBUG, msg, **kwargs)
    
    def info(self, msg: str, **kwargs) -> None:
        """记录信息日志"""
        self._log_async(logging.INFO, msg, **kwargs)
    
    def warning(self, msg: str, alert: bool = False, **kwargs) -> None:
        """记录警告日志"""
        if alert:
            kwargs['extra'] = kwargs.get('extra', {})
            kwargs['extra']['alert'] = True
        self._log_async(logging.WARNING, msg, **kwargs)
    
    def error(self, msg: str, exc_info: Optional[Exception] = None, **kwargs) -> None:
        """记录错误日志"""
        if exc_info is not None and EXCEPTION_TRACE_ENABLED:
            kwargs['exc_info'] = exc_info
        self._log_async(logging.ERROR, msg, **kwargs)
    
    def critical(self, msg: str, exc_info: Optional[Exception] = None, **kwargs) -> None:
        """记录严重错误日志"""
        if exc_info is not None and EXCEPTION_TRACE_ENABLED:
            kwargs['exc_info'] = exc_info
        self._log_async(logging.CRITICAL, msg, **kwargs)
    
    def exception(self, msg: str, **kwargs) -> None:
        """记录异常日志，自动包含异常信息"""
        self.error(msg, exc_info=True, **kwargs)
    
    def update_level(self, level: LogLevel) -> None:
        """更新日志级别
        
        Args:
            level: 新的日志级别
        """
        from .config import update_log_level
        update_log_level(level)
        self.logger.setLevel(get_log_level())
    
    def close(self) -> None:
        """关闭日志处理器"""
        if ASYNC_ENABLED and self.log_queue is not None:
            try:
                self.log_queue.put(None)  # 发送停止信号
                if self.worker_thread and self.worker_thread.is_alive():
                    self.worker_thread.join(timeout=5)
            except:
                pass
        
        # 关闭所有处理器
        for handler in self.logger.handlers[:]:
            handler.close()
            self.logger.removeHandler(handler)